package com.wongoing.api.wechat.pub;

import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Formatter;
import java.util.UUID;

import com.alibaba.fastjson.JSON;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.wongoing.api.HttpRequestUtil;
import com.wongoing.api.wechat.pub.entity.AccessToken;
import com.wongoing.api.wechat.pub.entity.JsapiTicket;
import com.wongoing.api.wechat.pub.entity.MenuParam;
import com.wongoing.api.wechat.pub.entity.PageAccessToken;
import com.wongoing.api.wechat.pub.entity.ResultMsg;
import com.wongoing.api.wechat.pub.entity.SendMsg;
import com.wongoing.api.wechat.pub.entity.UserInfo;
import com.wongoing.sys.shiro.ShiroHelper;

/**
 * 功能说明：微信公众号接口调用封装类，封装所有微信公众号接口调用方法
 * 修改说明：
 * @author zhenglibing
 * @date 2018年1月9日 上午9:04:34
 * @version 0.1
 */
public class WebChat {
	
	/**
	 * 功能说明：基础方法，时间戳方法
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月25日 上午9:38:45
	 * @return 返回当前时间的时间戳字符串
	 */
	public static String getTimestamp() {
        return Long.toString(System.currentTimeMillis());
    }
	
	/**
	 * 功能说明：基础方法，获取nonceStr随机字符串
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月25日 上午9:46:14
	 * @return
	 */
	public static String getNonceStr() {
        return UUID.randomUUID().toString();
    }
	/**
	 * 功能说明：基础方法：在回调模式下验证服务器接口地址
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 上午11:03:15
	 * @param token 公众号号的全局唯一票据
	 * @param sEncodingAESKey 微信公众号EncodingAESKey
	 * @param appId 微信公众号AppId
	 * @param msg_signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
	 * @param timestamp 时间戳
	 * @param nonce 随机串，对应URL参数的nonce
	 * @return 验证成功返回true，否则返回false
	 * @throws AesException
	 */
	public static boolean checkSignature(String token, String sEncodingAESKey, String appId, String msg_signature, String timestamp, String nonce) throws AesException
    {
        WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, sEncodingAESKey, appId);
        boolean result = wxcpt.verifyUrl(msg_signature, timestamp, nonce);
        return result;
    }
	
	/**
	 * 功能说明：基础方法：检验消息的真实性，并且获取解密后的明文.
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 上午11:09:17
	 * @param token 应用的回调接口票证
	 * @param sEncodingAESKey 应用的回调接口密钥
	 * @param appId AppId
	 * @param msg_signature 微信加密签名，msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体，必填参数
	 * @param timestamp 时间戳，必填参数
	 * @param nonce 随机数，必填参数
	 * @param postData 加密的随机字符串，以msg_encrypt格式提供。需要解密并返回echostr明文，解密后有random、msg_len、msg、$CorpID四个字段，其中msg即为echostr明文。首次校验时必填
	 * @return 返回解密后字符串
	 * @throws AesException
	 */
	public static String serverDecryptMsg(String token, String sEncodingAESKey, String appId, String msg_signature, String timestamp, String nonce, String postData) throws AesException
    {
        WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, sEncodingAESKey, appId);
        String sEchoStr = wxcpt.decryptMsg(msg_signature, timestamp, nonce, postData);
        return sEchoStr;
    }
	
	/**
	 * 功能说明：基础方法：将公众平台回复用户的消息加密打包.
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 上午11:28:04
	 * @param token 应用的回调接口票证
	 * @param sEncodingAESKey 应用的回调接口密钥
	 * @param appId AppId
	 * @param msg_signature 微信加密签名，msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体，必填参数
	 * @param timestamp 时间戳，必填参数
	 * @param nonce 随机数，必填参数
	 * @param replyMsg 要加密的回复消息
	 * @return 返回加密后的密文
	 * @throws AesException
	 */
	public static String ServerEncryptMsg(String token, String sEncodingAESKey, String appId, String msg_signature, String timestamp, String nonce, String replyMsg) throws AesException
    {
        WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, sEncodingAESKey, appId);
        String sEchoStr = wxcpt.encryptMsg(replyMsg, timestamp, nonce);
        return sEchoStr;
    }
	
	/**
	 * 功能说明：基础方法：获取AccessToken
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 上午11:32:50
	 * @param appid 微信公众号AppId
	 * @param secret 凭证密钥
	 * @return 返回公众号号的全局唯一票据
	 * @throws IOException
	 */
	public static String getAccessToken(String appid, String secret) throws IOException
    {
        String param = "grant_type=client_credential&appid=%s&secret=%s";
        param = String.format(param, appid, secret);
        String jsonAccessToken = HttpRequestUtil.sendGet(Global.ACCESSTOKENURL, param);
        return jsonAccessToken;
    }
	
	/**
	 * 功能说明：基础方法：获取AccessToken实体
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 上午11:34:44
	 * @param appid 微信公众号AppId
	 * @param secret 凭证密钥
	 * @return 返回公众号的全局唯一票据实体
	 * @throws IOException
	 */
	public static AccessToken getAccessTokenEntity(String appid, String secret) throws IOException
    {
        String jsonResult = getAccessToken(appid, secret);
        AccessToken result = JSON.parseObject(jsonResult, AccessToken.class);
        return result;
    }
	
	/**
	 * 功能说明：基础方法，获取网页授权access_token
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月15日 上午9:16:42
	 * @param appid 微信公众号AppId
	 * @param secret 凭证密码
	 * @param code 引导网页授权的code码
	 * @return 返回网页授权json字符串
	 * @throws IOException
	 */
	public static String getPageAccessToken(String appid, String secret, String code) throws IOException {
		String param = "appid=%s&secret=%s&code=%s&grant_type=authorization_code";
		param = String.format(param, appid, secret, code);
		String jsonResult = HttpRequestUtil.sendGet(Global.GETPAGEACCESSTOKENURL, param);
		return jsonResult;
	}
	
	/**
	 * 功能说明：基础方法，获取网页授权access_token
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月15日 上午9:18:30
	 * @param appid 微信公众号AppId
	 * @param secret 凭证密码
	 * @param code 引导网页授权的code码
	 * @return 返回网页授权json字符串
	 * @throws IOException
	 */
	public static PageAccessToken getPageAccessTokenEntity(String appid, String secret, String code) throws IOException {
		String jsonResult = getPageAccessToken(appid, secret, code);
		PageAccessToken result = JSON.parseObject(jsonResult, PageAccessToken.class);
		return result;
	}
	
	/**
	 * 功能说明：素材方法：获取临时素材文件（不包括视频）
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午1:32:02
	 * @param accessToken 微信公众号的全局唯一票据
	 * @param media_id 要获取的素材文件的media_id
	 * @return 返回对应素材文件的json字符串
	 * @throws IOException
	 */
	public static String getTempMedia(String accessToken, String media_id) throws IOException
    {
        String param = "access_token=%s&media_id=%s";
        param = String.format(param, accessToken, media_id);
        String jsonResult = HttpRequestUtil.sendGet(Global.GETTEMPMEDIAURL, param);
        return jsonResult;
    }
	
	/**
	 * 功能说明：素材方法：下载临时素材文件
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:10:05
	 * @param accessToken 微信公众号的全局唯一票据
	 * @param media_id 要下载的媒体文件的media_id
	 * @param outFileName 输出文件的完整路径包括文件名
	 * @return 成功返回文件名，失败返回“”
	 * @throws IOException
	 */
	public static String downloadTempMedia(String accessToken, String media_id, String outFileName) throws IOException
    {
        String param = "access_token=%s&media_id=%s";
        param = String.format(param, accessToken, media_id);
        boolean result = HttpRequestUtil.downloadFile(Global.GETTEMPMEDIAURL, param, outFileName);
        if (result)
        {
        	File file = new File(outFileName);
            return file.getName();
        }
        else
        {
            return "";
        }
    }
	
	/**
	 * 功能说明：客服消息方法：发送文本消息
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:12:50
	 * @param accessToken 企业号的全局唯一票据
	 * @param jsonData 要发送的json格式的消息
	 * @return 返回json结果字符串
	 * @throws IOException
	 */
	public static String sendTextMsg1(String accessToken, String jsonData) throws IOException
    {
        String url = Global.SENDMSGURL + "?access_token=%s";
        url = String.format(url, accessToken);
        String jsonResult = HttpRequestUtil.sendPost(url, jsonData);
        return jsonResult;
    }
	
	/**
	 * 功能说明：客服消息方法：发送文本消息
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:19:31
	 * @param accessToken 企业号的全局唯一票据
	 * @param jsonData 要发送的json格式的消息
	 * @return 返回ResultMsg结果对象
	 * @throws IOException
	 */
	public static ResultMsg sendTextMsg(String accessToken, String jsonData) throws IOException
    {
        String url = Global.SENDMSGURL + "?access_token=%s";
        url = String.format(url, accessToken);
        String jsonResult = HttpRequestUtil.sendPost(url, jsonData);
        ResultMsg result = JSON.parseObject(jsonResult, ResultMsg.class);
        return result;
    }
	
	/**
	 * 功能说明：客服消息方法：发消息（包括文本消息、图像、声音、视频、文件、图文、微信后台图文）
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:20:16
	 * @param accessToken 企业号的全局唯一票据
	 * @param msg 要发送的消息对象
	 * @return 返回ResultMsg结果对象
	 * @throws IOException
	 */
	public static ResultMsg sendMsg(String accessToken, SendMsg msg) throws IOException
    {
        ResultMsg result = sendTextMsg(accessToken, msg.toJsonString());
        return result;
    }
	
	/**
	 * 功能说明：菜单方法：创建菜单
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:24:10
	 * @param accessToken 公众号的唯一票据
	 * @param param 要创建的菜单参数
	 * @return 返回创建结果对象
	 * @throws IOException
	 */
	public static ResultMsg createMenu(String accessToken, MenuParam param) throws IOException
    {
        String url = Global.CREATEMENUURL + "?access_token=%s";
        url = String.format(url, accessToken);
        String jsonResult = HttpRequestUtil.sendPost(url, param.toJsonString());
        ResultMsg result = JSON.parseObject(jsonResult, ResultMsg.class);
        return result;
    }
	
	/**
	 * 功能说明：菜单方法：删除菜单
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:26:07
	 * @param accessToken 公众号的全局唯一票据
	 * @return 返回删除结果对象
	 * @throws IOException
	 */
	public static ResultMsg delMenu(String accessToken) throws IOException
    {
        String param = "access_token=%s";
        param = String.format(param, accessToken);
        String jsonResult = HttpRequestUtil.sendGet(Global.DELMENURUL, param);
        ResultMsg result = JSON.parseObject(jsonResult, ResultMsg.class);
        return result;
    }
	
	/**
	 * 功能说明：菜单方法：获取菜单列表
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:27:23
	 * @param accessToken 企业号的全局唯一票据
	 * @return 返回菜单列表
	 * @throws IOException
	 */
	public static String getMenu(String accessToken) throws IOException
    {
        String param = "access_token=%s";
        param = String.format(param, accessToken);
        String jsonResult = HttpRequestUtil.sendGet(Global.GETMENUURL, param);
        return jsonResult;
    }
	
	/**
	 * 功能说明：用户管理方法：获取用户列表
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:30:50
	 * @param accessToken 公众号的全局唯一票据
	 * @param next_openid 第一个拉取的OPENID，不填默认从头开始拉取
	 * @return 返回json结果字符串
	 * @throws IOException
	 */
	public static String getUserJsonStr(String accessToken, String next_openid) throws IOException
    {
        String param = "access_token=%s&next_openid=%s";
        param = String.format(param, accessToken, next_openid);
        String jsonResult = HttpRequestUtil.sendGet(Global.GETUSERURL, param);
        return jsonResult;
    }
	
	/**
	 * 功能说明：用户管理方法：获取用户列表
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:32:54
	 * @param accessToken 公众号的全局唯一票据
	 * @param next_openid 第一个拉取的OPENID，不填默认从头开始拉取
	 * @return 返回结果对象
	 * @throws IOException
	 */
	public static ResultMsg getUser(String accessToken, String next_openid) throws IOException
    {
        String jsonResult = getUserJsonStr(accessToken, next_openid);
        ResultMsg result = JSON.parseObject(jsonResult, ResultMsg.class);
        return result;
    }
	
	/**
	 * 功能说明：用户管理方法：获取用户基本信息（包括UnionID机制）
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:34:40
	 * @param accessToken 公众号的全局唯一票据
	 * @param openid 普通用户的标识，对当前公众号唯一
	 * @param lang 国家地区语言版本，zh_CN简体,zh_TW繁体,en英语
	 * @return 返回json结果字符串
	 * @throws IOException
	 */
	public static String getUserInfoJsonStr(String accessToken, String openid, String lang) throws IOException
    {
        String param = "access_token=%s&openid=%s&lang=%s";
        param = String.format(param, accessToken, openid, lang);
        String jsonResult = HttpRequestUtil.sendGet(Global.GETUSERINFOURL, param);
        return jsonResult;
    }
	
	/**
	 * 功能说明：用户管理方法：获取用户基本信息（包括UnionID机制）
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:36:39
	 * @param accessToken 公众号的全局唯一票据
	 * @param openId 普通用户的标识，对当前公众号唯一
	 * @param lang 国家地区语言版本，zh_CN简体,zh_TW繁体,en英语
	 * @return 返回UserInfo实体对象
	 * @throws IOException
	 */
	public static UserInfo getUserInfo(String accessToken, String openId, String lang) throws IOException
    {
        String jsonResult = getUserInfoJsonStr(accessToken, openId, lang);
        UserInfo result = JSON.parseObject(jsonResult, UserInfo.class);
        return result;
    }
	
	/**
	 * 功能说明：用户管理方法：获取用户基本信息（包括UnionID机制）
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月9日 下午2:38:00
	 * @param accessToken 公众号的全局唯一票据
	 * @param openId 普通用户的标识，对当前公众号唯一
	 * @return 返回UserInfo实体对象
	 * @throws IOException
	 */
	public static UserInfo getUserInfo(String accessToken, String openId) throws IOException
    {
        UserInfo result = getUserInfo(accessToken, openId, "zh_CN");
        return result;
    }
	
	/**
	 * 功能说明：基础方法：获取JS-SDK ticket
	 * 修改说明：
	 * @author liulh
	 * @date 2018年1月24日 15:32:50
	 * @param accessToken 公众号的全局唯一票据
	 * @return 返回JSAPI_TICKET json字符串
	 * @throws IOException
	 */
	public static String getJsApiTicketJsonStr(String accessToken) throws IOException {
        String param = "access_token=%s&type=jsapi";
        param = String.format(param, accessToken);
        String jsonResult = HttpRequestUtil.sendGet(Global.TICKETURL, param);
        return jsonResult;
    }
	
	/**
	 * 功能说明：
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月25日 上午9:05:22
	 * @param accessToken
	 * @return 返回JSAPI_TICKET 实体对象
	 * @throws IOException
	 */
	public static JsapiTicket getJsApiTicket(String accessToken) throws IOException {
		String jsonResult = getJsApiTicketJsonStr(accessToken);
		JsapiTicket result = JSON.parseObject(jsonResult, JsapiTicket.class);
		return result;
	}
	
	/**
	 * 功能说明：获取JsSdk签名
	 * 修改说明：
	 * @author zhenglibing
	 * @date 2018年1月25日 上午10:11:31
	 * @param jsapi_ticket jsapi票证
	 * @param timestamp 系统时间戳
	 * @param nonce_str 随机字符串
	 * @param url 当前页面地址
	 * @return
	 */
	public static String getJsSdkSign(String jsapi_ticket, String timestamp, String nonce_str,  String url) {
		 
        String signature = "";
        //1、对所有待签名参数按照字段名的ASCII 码从小到大排序（字典序）
        String[] arr = {"jsapi_ticket=" + jsapi_ticket, "timestamp=" + timestamp, "noncestr=" + nonce_str, "url=" + url};
        java.util.Arrays.sort(arr);
        //2、使用URL键值对的格式（即key1=value1&key2=value2…）拼接成字符串string1。这里需要注意的是所有参数名均为小写字符
        String string1 = String.join("&", arr);
        //3、对string1作sha1加密，字段名和字段值都采用原始值，不进行URL 转义
        signature = ShiroHelper.sha1Encrypt(string1, "", "", 1).toLowerCase();
        
//        //注意这里参数名必须全部小写，且必须有序
//        String string1 = "jsapi_ticket=" + jsapi_ticket +
//                  "&noncestr=" + nonce_str +
//                  "&timestamp=" + timestamp +
//                  "&url=" + url;
//        try {
//            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
//            crypt.reset();
//            crypt.update(string1.getBytes("UTF-8"));
//            signature = byteToHex(crypt.digest());
//        } catch (Exception e) {
//        	System.out.println(e);
//        }

       return signature;
    }
	
//	public static String byteToHex(final byte[] hash) {
//        Formatter formatter = new Formatter();
//        for (byte b : hash){
//            formatter.format("%02x", b);
//        }
//        String result = formatter.toString();
//        formatter.close();
//        return result;
//    }
}
